#include "stdafx.h"
#include "Certification.h"

#include "../../NSNetLibSrc/Library/BSLib/NetEngine/KeyGenerator.h"

#include "../../NSNetLibSrc/Framework/ServerFramework/ServerFramework_Database.h"
#include "../../NSNetLibSrc/Framework/ServerFramework/Registry.h"

#include "../../NSNetLibSrc/ThirdPartyLib/TinyXML/tinyxml.h"

#ifdef _DEBUG
	#ifdef _WIN64
		#pragma comment(lib, "tinyxmldx64.lib" )
	#else
		#pragma comment(lib, "tinyxmld.lib" )
	#endif
#else
	#ifdef _WIN64
		#pragma comment(lib, "tinyxmlx64.lib" )
	#else
		#pragma comment(lib, "tinyxml.lib" )
	#endif
#endif

//-----------------------------------------------------------------------------
BS_IMPLEMENT_DYNCREATE( CCertificationMainProcess, CServerProcessMain);

IMPLEMENT_WINMAIN( ServerFramework::eMT_Window, L"Certification", &g_CertificationServer);

CCertificationServer::CCertificationServer()
{
	m_pDivisionList = NULL;
	m_pServerMachineList = NULL;
	m_pServerBodyList = NULL;
	m_pServerCordList = NULL;
	m_pModuleList = NULL;
}

//----------------- 
bool CCertificationServer::InitializeServerTask_Main()
{
	SetDebugHelperHandlers();

	if( !g_pNetEngine->RegisterTask( SFTASK_PROCESSMAIN, NULL, BS_RUNTIME_CLASS( CCertificationMainProcess), NULL))
		return false;
	if( !g_pNetEngine->ActivateService( SFTASK_PROCESSMAIN, 1, THREAD_PRIORITY_NORMAL, 0, 0, NULL))
		return false;

	return true;
}

//----------------- 
bool CCertificationServer::InitializeLocalData()
{
	if( !CServerApp::InitializeLocalData())
		return false;

	m_CRC.Initialize();

	RetrieveArchitectureDataFromXML();

	ReportLog( LOG_NOTIFY_FILE, TEXT("CertificationServer successfully started!") );

	return true;
}

//-----------------
bool CCertificationServer::UninitializeLocalData()
{
	CServerApp::UninitializeLocalData();
	return true;
}

//-----------------
bool CCertificationServer::RetrieveArchitectureDataFromXML()
{
	std::wstring strwCertXMLFilePath = g_ServerLaunchInfo.m_szModulePath;
	strwCertXMLFilePath += L"\\certification.xml";

	std::string strCertXMLFilePath;

	ConversionANSI( strwCertXMLFilePath, strCertXMLFilePath );

	TiXmlDocument certification_doc;
	if( certification_doc.LoadFile(strCertXMLFilePath.c_str()) == false )
		return false;

	m_mapArchitectureHandlers.insert( MAP_VARIABLE_HANDLER::value_type(std::string("Module"), &CCertificationServer::Certification_Module) );
	m_mapArchitectureHandlers.insert( MAP_VARIABLE_HANDLER::value_type(std::string("ServerMachine"), &CCertificationServer::Certification_ServerMachine) );
	m_mapArchitectureHandlers.insert( MAP_VARIABLE_HANDLER::value_type(std::string("Division"), &CCertificationServer::Certification_Division) );
	m_mapArchitectureHandlers.insert( MAP_VARIABLE_HANDLER::value_type(std::string("ServerBody"), &CCertificationServer::Certification_ServerBody) );
	m_mapArchitectureHandlers.insert( MAP_VARIABLE_HANDLER::value_type(std::string("ServerCord"), &CCertificationServer::Certification_ServerCord) );
	m_mapArchitectureHandlers.insert(std::make_pair(std::string("DedicatedCoordinate"), &CCertificationServer::Certification_DedicatedCoordinate));

	m_pDivisionList = new LIST_Division;
	m_pServerMachineList = new LIST_ServerMachine;
	m_pServerBodyList = new LIST_ServerBody;
	m_pServerCordList = new LIST_ServerCord;
	m_pModuleList = new LIST_Module;
	dedicated_groups_ = new LIST_DedicatedGroup;
	dedicated_ip_bounds_ = new LIST_DedicatedIPBound;

	//let's parse server architecture data file!
	TiXmlElement* pElements = certification_doc.FirstChildElement();
	for( ; pElements != NULL ; pElements = pElements->NextSiblingElement() )
	{
		std::string strElement = pElements->Value();

		MAP_VARIABLE_HANDLER::iterator it = m_mapArchitectureHandlers.find( strElement );
		if( it != m_mapArchitectureHandlers.end( ))
		{	
			VARIABLE_HANDLER fpFunc = ((*it).second);
			(this->*fpFunc)( pElements );
		}
	}

	if( !RegisterModule( m_pModuleList))
	{
		ReportLog( LOG_FATAL, _T("cannot register module"));
		return false;
	}

	if( !BuildServerArchitecture(	m_pDivisionList,
									m_pServerMachineList,
									m_pServerBodyList,
									m_pServerCordList,
									dedicated_groups_,
									dedicated_ip_bounds_))
	{
		ReportLog( LOG_FATAL, _T("cannot build server architecture"));
		return false;
	}

	return true;
}

void CCertificationServer::Certification_Module( TiXmlElement* pElement )
{
	TiXmlElement* pChild = pElement->FirstChildElement();
	for( ; pChild != NULL ; pChild = pChild->NextSiblingElement() )
	{
		const char* pName = pChild->Attribute( "ModuleName" );
		const char* pType = pChild->Attribute( "ModuleType" );
		if( pName == NULL || pType == NULL )
			continue;

		sModule* pModule = new sModule;

		std::string strName = pName;
		ConversionUnicode( strName, pModule->m_strName );

		std::string strModuleType = pType;
		ConversionUnicode( strModuleType, pModule->m_strModuleType );

		pModule->m_nID = m_CRC.Generate32( (LPBYTE)pName, (DWORD)::strlen(pName) );
		
		m_pModuleList->push_back( pModule );
	}
}

void CCertificationServer::Certification_ServerMachine( TiXmlElement* pElement )
{
	TiXmlElement* pChild = pElement->FirstChildElement();
	for( ; pChild != NULL ; pChild = pChild->NextSiblingElement() )
	{
		const char* pName = pChild->Attribute( "ServerMachineName" );
		if( pName == NULL )
			continue;

		sServerMachine* pMachine = new sServerMachine;

		std::string strTemp = pName;
		ConversionUnicode( strTemp, pMachine->m_strName );

		strTemp = pChild->Attribute( "DivisionName" );
		ConversionUnicode( strTemp, pMachine->m_strDivisionName );

		strTemp = pChild->Attribute( "PublicIP" );
		ConversionUnicode( strTemp, pMachine->m_strPublicIP );

		strTemp = pChild->Attribute( "PrivateIP" );
		ConversionUnicode( strTemp, pMachine->m_strPrivateIP );

		m_pServerMachineList->push_back( pMachine );
	}
}

void CCertificationServer::Certification_Division( TiXmlElement* pElement )
{
	TiXmlElement* pChild = pElement->FirstChildElement();
	for( ; pChild != NULL ; pChild = pChild->NextSiblingElement() )
	{
		const char* pName = pChild->Attribute( "DivisionName" );
		if( pName == NULL )
			continue;

		sDivision* pDivision = new sDivision;

		std::string strTemp = pName;
		ConversionUnicode( strTemp, pDivision->m_strName );

		strTemp = pChild->Attribute( "DBConfig" );
		ConversionUnicode( strTemp, pDivision->m_strDBConfig );

		strTemp = pChild->Attribute( "BladesDBConfig" );
		ConversionUnicode( strTemp, pDivision->m_strBladesDBConfig );

		strTemp = pChild->Attribute( "BladesLogDBConfig" );
		ConversionUnicode( strTemp, pDivision->m_strBladesLogDBConfig );

		m_pDivisionList->push_back( pDivision );
	}
}

void CCertificationServer::Certification_ServerBody( TiXmlElement* pElement )
{
	//*
	TiXmlElement* pChild = pElement->FirstChildElement();
	for( ; pChild != NULL ; pChild = pChild->NextSiblingElement() )
	{
		const char* pName = pChild->Attribute( "ServerBodyName" );
		if( pName == NULL )
			continue;

		sServerBody* pBody = new sServerBody;

		std::string strTemp = pName;
		ConversionUnicode( strTemp, pBody->m_strName );

		strTemp = pChild->Attribute( "DivisionName" );
		ConversionUnicode( strTemp, pBody->m_strDivisionName );

		strTemp = pChild->Attribute( "ServerMachineName" );
		ConversionUnicode( strTemp, pBody->m_strServerMachineName );

		strTemp = pChild->Attribute( "ModuleName" );
		ConversionUnicode( strTemp, pBody->m_strModuleName );

		strTemp = pChild->Attribute( "CertifierName" );
		ConversionUnicode( strTemp, pBody->m_strCertifierBodyName );

		pBody->m_nListenerPort = ::atoi( pChild->Attribute( "ListenPort" ) );

		pBody->m_nID = m_CRC.Generate32( (LPBYTE)pName, (DWORD)::strlen(pName) );
		pBody->m_nExecutionStep = eSES_Blind;
		pBody->m_pModule = NULL;
		pBody->m_pMachine = NULL;
		pBody->m_pDivision = NULL;

		m_pServerBodyList->push_back( pBody );
	}
	//*/
}

void CCertificationServer::Certification_ServerCord( TiXmlElement* pElement )
{
	//*
	TiXmlElement* pChild = pElement->FirstChildElement();
	for( ; pChild != NULL ; pChild = pChild->NextSiblingElement() )
	{
		const char* pInletName = pChild->Attribute( "Inlet" );
		if( pInletName == NULL )
			continue;

		const char* pOutletName = pChild->Attribute( "Outlet" );
		if( pOutletName == NULL )
			continue;

		const char* pBindType = pChild->Attribute( "BindType" );
		if( pBindType == NULL )
			continue;

		sServerCord* pCord = new sServerCord;

		std::string strTemp = pInletName;
		ConversionUnicode( strTemp, pCord->m_strInletBodyName );

		strTemp = pOutletName;
		ConversionUnicode( strTemp, pCord->m_strOutletBodyName );

		if (lstrcmpA(pBindType, "private") == 0)
			pCord->m_nBindType = 1;
		else
			pCord->m_nBindType = 0;

		std::wstring strCordID = pCord->m_strInletBodyName + pCord->m_strOutletBodyName;
		pCord->m_nID = m_CRC.Generate32( (LPBYTE)strCordID.c_str(), (DWORD)strCordID.size() );

		m_pServerCordList->push_back( pCord );
	}
	//*/
}

void CCertificationServer::Certification_DedicatedCoordinate(TiXmlElement* element)
{
	// machine group
	TiXmlElement* node_machine_group = element->FirstChildElement();
	if (node_machine_group == nullptr || ::strcmp(node_machine_group->Value(), "MachineGroup") != 0)
	{
		ReportLog(LOG_FATAL_FILE, L"Load dedicated coordinator failed.");
		return;
	}

	TiXmlElement* child_machine_group = node_machine_group->FirstChildElement();
	for (; child_machine_group != NULL ; child_machine_group = child_machine_group->NextSiblingElement())
	{
		const char* server_body_name = child_machine_group->Attribute("ServerBodyName");
		const char* allowed_ip_begin = child_machine_group->Attribute("AllowedIPBegin");
		const char* allowed_ip_end = child_machine_group->Attribute("AllowedIPEnd");
		const char* port = child_machine_group->Attribute("Port");
		
		if (server_body_name == nullptr || allowed_ip_begin == nullptr || allowed_ip_end == nullptr || port == nullptr)
		{
			ReportLog(LOG_FATAL_FILE, L"Dedicated coordinate not set.");
			continue;
		}

		sDedicatedGroup* group = new sDedicatedGroup;
		group->ip_begin = ::inet_addr(allowed_ip_begin);
		group->ip_end = ::inet_addr(allowed_ip_end);
		group->port = ::atoi(port);

		if (group->ip_begin == INADDR_NONE || 
			group->ip_end == INADDR_NONE ||
			group->ip_begin > group->ip_end)
		{
			delete group;
			continue;
		}

		ConversionUnicode(server_body_name, group->server_body_name);

		dedicated_groups_->push_back(group);
	}

	// ip bound
	TiXmlElement* node_ip_bound = node_machine_group->NextSiblingElement();
	if (node_ip_bound == nullptr || ::strcmp(node_ip_bound->Value(), "IPBound") != 0)
		return;

	TiXmlElement* child_ip_bound = node_ip_bound->FirstChildElement();
	for (; child_ip_bound != NULL ; child_ip_bound = child_ip_bound->NextSiblingElement())
	{
		const char* internal_ip = child_ip_bound->Attribute("InternalIP");
		const char* service_ip = child_ip_bound->Attribute("ServiceIP");

		if (internal_ip == nullptr || service_ip == nullptr)
			continue;

		sDedicatedIPBound* ip_bound = new sDedicatedIPBound;
		ip_bound->internal_ip = ::inet_addr(internal_ip);
		ip_bound->service_ip = ::inet_addr(service_ip);

		if (ip_bound->internal_ip == INADDR_NONE || 
			ip_bound->service_ip == INADDR_NONE)
		{
			delete ip_bound;
			continue;
		}

		dedicated_ip_bounds_->push_back(ip_bound);
	}
}
